﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Joy.Erp.BasicArchives.ItemMasters;
using Joy.Erp.Ufida.Core;
using Microsoft.Extensions.Logging;

namespace Joy.Erp.Ufida.ItemMasters
{
    public class UnitGroupSyncAppService : UfidaAppService, IUnitGroupSyncAppService
    {
        protected IUnitRepository UnitRepository => LazyServiceProvider.LazyGetRequiredService<IUnitRepository>();
        protected IUnitAppService UnitAppService => LazyServiceProvider.LazyGetRequiredService<IUnitAppService>();
        protected IUnitGroupRepository UnitGroupRepository => LazyServiceProvider.LazyGetRequiredService<IUnitGroupRepository>();
        protected IUomUfidaAppService UomUfidaAppService => LazyServiceProvider.LazyGetRequiredService<IUomUfidaAppService>();

        public async Task<AnalysisDto> AnalyzeAsync()
        {
            var analysisDto = new AnalysisDto();
            analysisDto.Id = typeof(UnitGroup).FullName;

            var u9list = await UomUfidaAppService.GetAllAsync();
            var u9Ids = u9list.Select(x => x.GroupId.ToString()).Distinct().ToHashSet();
            analysisDto.U9Count = u9Ids.Count;

            var pmcQuery = await UnitGroupRepository.GetQueryableAsync();
            analysisDto.Count = await AsyncExecuter.CountAsync(pmcQuery.Where(x => !x.IsDeleted));

            var syncedSourceKeys = await AsyncExecuter.ToListAsync(
                pmcQuery
                    .Where(x => !x.IsDeleted && x.SourceType == SourceConfig.SourceType)
                    .Select(x => x.SourceKey)
            );

            analysisDto.AsyncCount = syncedSourceKeys.Count(key => u9Ids.Contains(key));
            analysisDto.UnasyncCount = analysisDto.U9Count - analysisDto.AsyncCount;
            analysisDto.SelfCount = analysisDto.Count - analysisDto.AsyncCount;

            return analysisDto;
        }

        public async Task<List<long>> GetUnasyncIds()
        {
            var u9list = await UomUfidaAppService.GetAllAsync();
            var u9Ids = u9list.Select(x => x.GroupId).Distinct().ToList();

            var pmcQuery = await UnitGroupRepository.GetQueryableAsync();
            var syncedSourceKeys = await AsyncExecuter.ToListAsync(
                pmcQuery
                    .Where(x => !x.IsDeleted && x.SourceType == SourceConfig.SourceType)
                    .Select(x => x.SourceKey)
            );

            var syncedSourceKeySet = new HashSet<string>(syncedSourceKeys);
            var unasyncIds = u9Ids
                .Where(id => !syncedSourceKeySet.Contains(id.ToString()))
                .ToList();

            return unasyncIds;
        }

        public async Task<int> CreateByIds‌Async(long[] ids)
        {
            if (ids == null || ids.Length == 0)
                return 0;

            var pmcQuery = await UnitGroupRepository.GetQueryableAsync();
            var idStrings = ids.Select(id => id.ToString()).ToList();
            var existingSourceKeys = await AsyncExecuter.ToListAsync(
                pmcQuery
                    .Where(x =>
                        idStrings.Contains(x.SourceKey) && x.SourceType == SourceConfig.SourceType
                    )
                    .Select(x => x.SourceKey)
            );
            var existingSet = new HashSet<string>(existingSourceKeys);

            var needCreateIds = ids
                .Where(id => !existingSet.Contains(id.ToString()))
                .Distinct()
                .ToList();
            if (needCreateIds.Count == 0)
                return ids.Length;

            Logger.LogInformation($"[批量同步-单位组] 开始处理 {needCreateIds.Count} 条单位组，总共 {ids.Length} 条（已存在 {existingSet.Count} 条）");

            var allU9DataDict = new Dictionary<long, List<UomUfidaDto>>();
            foreach (var id in needCreateIds)
            {
                try
                {
                    var items = await UomUfidaAppService.GetItemsAsync(id);
                    if (items != null && items.Count > 0)
                    {
                        allU9DataDict[id] = items;
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogWarning(ex, $"[批量同步-单位组] 获取U9数据失败，Id: {id}");
                }
            }

            if (allU9DataDict.Count == 0) return existingSet.Count;

            var allUnitSourceKeys = allU9DataDict.Values.SelectMany(x => x).Select(x => x.ID.ToString()).Distinct().ToList();
            var unitCacheDict = await BatchLoadUnits(allUnitSourceKeys);

            var entities = new List<UnitGroup>();
            foreach (var kvp in allU9DataDict)
            {
                try
                {
                    var entity = await BuildUnitGroupEntityAsync(kvp.Key, kvp.Value, unitCacheDict);
                    if (entity != null)
                    {
                        entities.Add(entity);
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogWarning(ex, $"[批量同步-单位组] 创建实体失败，Id: {kvp.Key}");
                }
            }

            var successCount = 0;
            if (entities.Count > 0)
            {
                using (var uow = UnitOfWorkManager.Begin(new Volo.Abp.Uow.AbpUnitOfWorkOptions(), requiresNew: true))
                {
                    try
                    {
                        await UnitGroupRepository.BulkInsertAsync(entities, autoSave: false);
                        await uow.CompleteAsync();
                        successCount = entities.Count;
                    }
                    catch (Exception ex)
                    {
                        Logger.LogWarning(ex, $"[批量同步-单位组] 批量插入失败，{ex.Message}");
                    }
                }
            }

            Logger.LogInformation($"[批量同步-单位组] 完成！成功创建 {successCount} 条，已存在 {existingSet.Count} 条，总计 {successCount + existingSet.Count} 条");

            return successCount + existingSet.Count;
        }

        public async Task<List<long>> GetSyncedIds()
        {
            var pmcQuery = await UnitGroupRepository.GetQueryableAsync();
            var syncedSourceKeys = await AsyncExecuter.ToListAsync(
                pmcQuery
                    .Where(x => !x.IsDeleted && x.SourceType == SourceConfig.SourceType)
                    .Select(x => x.SourceKey)
            );

            return syncedSourceKeys
                .Select(key => long.TryParse(key, out var parsedId) ? (long?)parsedId : null)
                .Where(id => id.HasValue)
                .Select(id => id.Value)
                .ToList();
        }

        public async Task<int> UpdateByIdsAsync(long[] ids)
        {
            if (ids == null || ids.Length == 0) return 0;

            Logger.LogInformation($"[批量同步-单位组] 开始处理 {ids.Length} 条单位组更新/删除");

            var pmcQuery = await UnitGroupRepository.WithDetailsAsync(x => x.Children);
            var idStrings = ids.Select(id => id.ToString()).ToList();
            var existingEntities = await AsyncExecuter.ToListAsync(
                pmcQuery.Where(x =>
                    idStrings.Contains(x.SourceKey) && x.SourceType == SourceConfig.SourceType
                )
            );

            if (existingEntities.Count == 0)
            {
                Logger.LogWarning($"[批量同步-单位组] 没有找到需要处理的记录");
                return 0;
            }

            var entityDict = existingEntities.ToDictionary(x => x.SourceKey, x => x);

            var allU9DataDict = new Dictionary<long, List<UomUfidaDto>>();
            var entitiesToDelete = new List<UnitGroup>();
            foreach (var id in ids)
            {
                if (!entityDict.ContainsKey(id.ToString())) continue;

                try
                {
                    var items = await UomUfidaAppService.GetItemsAsync(id);
                    if (items != null && items.Count > 0)
                    {
                        allU9DataDict[id] = items;
                    }
                    else
                    {
                        entitiesToDelete.Add(entityDict[id.ToString()]);
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogWarning(ex, $"[批量同步-单位组] 获取U9数据失败，Id: {id}");
                }
            }

            var entitiesToUpdate = new List<UnitGroup>();
            if (allU9DataDict.Count > 0)
            {
                var allUnitSourceKeys = allU9DataDict.Values.SelectMany(x => x).Select(x => x.ID.ToString()).Distinct().ToList();
                var unitCacheDict = await BatchLoadUnits(allUnitSourceKeys);

                foreach (var kvp in allU9DataDict)
                {
                    if (!entityDict.TryGetValue(kvp.Key.ToString(), out var entity)) continue;

                    var u9Items = kvp.Value;
                    if (u9Items.Count == 0) continue;

                    try
                    {
                        var baseUom = u9Items.FirstOrDefault(x => x.IsBase);
                        if (baseUom == null)
                        {
                            Logger.LogWarning($"[批量同步-单位组] 未找到主单位，SourceKey: {kvp.Key}");
                            continue;
                        }

                        var mainUnitDto = await EnsureUnitAsync(baseUom, unitCacheDict);
                        if (mainUnitDto == null)
                        {
                            Logger.LogWarning($"[批量同步-单位组] 未找到或创建主单位，SourceKey: {baseUom.ID}");
                            continue;
                        }

                        entity.Code = u9Items[0].GroupCode;
                        entity.GroupName = u9Items[0].GroupName;
                        entity.MainUnitId = mainUnitDto.Id;
                        entity.SourceCreatedOn = u9Items[0].CreatedOn;
                        entity.LastModificationTime = Clock.Now;
                        entity.LastModifierId = CurrentUser.Id;

                        var existingChildrenDict = entity.Children.ToDictionary(x => x.SourceKey);
                        var processedKeys = new HashSet<string>();

                        foreach (var item in u9Items)
                        {
                            var unitDto = await EnsureUnitAsync(item, unitCacheDict);
                            if (unitDto == null)
                            {
                                Logger.LogWarning($"[批量同步-单位组] 未找到单位，SourceKey: {item.ID}");
                                continue;
                            }

                            var sourceKey = item.ID.ToString();
                            var ratios = GetRatios(GetRate(item, u9Items));
                            processedKeys.Add(sourceKey);

                            if (existingChildrenDict.TryGetValue(sourceKey, out var child))
                            {
                                child.UnitId = unitDto.Id;
                                child.MainToAuxiliaryRatio = ratios.mainToAux;
                                child.AuxiliaryToMainRatio = ratios.auxToMain;
                                child.SourceCreatedOn = item.CreatedOn;
                                child.LastModificationTime = Clock.Now;
                                child.LastModifierId = CurrentUser.Id;
                            }
                            else
                            {
                                var childEntity = new AuxiliaryUnit
                                {
                                    SourceType = SourceConfig.SourceType,
                                    SourceKey = sourceKey,
                                    SourceCreatedOn = item.CreatedOn,
                                    UnitId = unitDto.Id,
                                    MainArchiveId = entity.Id,
                                    MainToAuxiliaryRatio = ratios.mainToAux,
                                    AuxiliaryToMainRatio = ratios.auxToMain,
                                    CreationTime = Clock.Now,
                                    CreatorId = CurrentUser.Id,
                                };
                                childEntity.SetId(GuidGenerator.Create());
                                entity.Children.Add(childEntity);
                            }
                        }

                        var childrenToRemove = entity.Children.Where(child => !processedKeys.Contains(child.SourceKey)).ToList();
                        foreach (var child in childrenToRemove)
                        {
                            entity.Children.Remove(child);
                        }

                        entitiesToUpdate.Add(entity);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogWarning(ex, $"[批量同步-单位组] 修改实体失败，Id: {kvp.Key}");
                    }
                }
            }

            var updateCount = 0;
            var deleteCount = 0;
            if (entitiesToUpdate.Count > 0 || entitiesToDelete.Count > 0)
            {
                using (var uow = UnitOfWorkManager.Begin(new Volo.Abp.Uow.AbpUnitOfWorkOptions(), requiresNew: true))
                {
                    try
                    {
                        if (entitiesToUpdate.Count > 0)
                        {
                            await UnitGroupRepository.BulkUpdateAsync(
                                entitiesToUpdate,
                                autoSave: false
                            );
                            updateCount = entitiesToUpdate.Count;
                        }

                        if (entitiesToDelete.Count > 0)
                        {
                            await UnitGroupRepository.BulkDeleteAsync(
                                entitiesToDelete,
                                autoSave: false
                            );
                            deleteCount = entitiesToDelete.Count;
                        }

                        await uow.CompleteAsync();
                    }
                    catch (Exception ex)
                    {
                        Logger.LogWarning(ex, $"[批量同步-单位组] 批量更新/删除失败，{ex.Message}");
                    }
                }
            }

            Logger.LogInformation($"[批量同步-单位组] 完成！成功更新 {updateCount} 条");
            if (deleteCount > 0)
            {
                Logger.LogInformation($"[批量同步-单位组] 删除 {deleteCount} 条记录");
            }

            return updateCount + deleteCount;
        }

        private async Task<UnitGroup> BuildUnitGroupEntityAsync(
            long groupId,
            List<UomUfidaDto> items,
            Dictionary<string, UnitDto> unitCacheDict
        )
        {
            if (items == null || items.Count == 0)
                return null;

            var baseUom = items.FirstOrDefault(x => x.IsBase);
            if (baseUom == null)
            {
                Logger.LogWarning($"[批量同步-单位组] 未找到主单位，SourceKey: {groupId}");
                return null;
            }

            var mainUnitDto = await EnsureUnitAsync(baseUom, unitCacheDict);
            if (mainUnitDto == null)
            {
                Logger.LogWarning($"[批量同步-单位组] 未找到或创建主单位，SourceKey: {baseUom.ID}");
                return null;
            }

            var entityId = GuidGenerator.Create();
            var entity = new UnitGroup
            {
                SourceType = SourceConfig.SourceType,
                SourceKey = groupId.ToString(),
                SourceCreatedOn = items[0].CreatedOn,
                Code = items[0].GroupCode,
                GroupName = items[0].GroupName,
                MainUnitId = mainUnitDto.Id,
                TenantId = CurrentTenant.Id,
                CreationTime = Clock.Now,
                CreatorId = CurrentUser.Id,
            };
            entity.SetId(entityId);

            foreach (var item in items)
            {
                var unitDto = await EnsureUnitAsync(item, unitCacheDict);
                if (unitDto == null)
                {
                    Logger.LogWarning($"[批量同步-单位组] 未找到单位，SourceKey: {item.ID}");
                    return null;
                }

                var ratios = GetRatios(GetRate(item, items));
                var child = new AuxiliaryUnit
                {
                    SourceType = SourceConfig.SourceType,
                    SourceKey = item.ID.ToString(),
                    SourceCreatedOn = item.CreatedOn,
                    UnitId = unitDto.Id,
                    MainArchiveId = entityId,
                    MainToAuxiliaryRatio = ratios.mainToAux,
                    AuxiliaryToMainRatio = ratios.auxToMain,
                    CreationTime = Clock.Now,
                    CreatorId = CurrentUser.Id,
                };
                child.SetId(GuidGenerator.Create());
                entity.Children.Add(child);
            }

            return entity;
        }

        private async Task<UnitDto> EnsureUnitAsync(
            UomUfidaDto uomUfidaDto,
            Dictionary<string, UnitDto> unitCacheDict
        )
        {
            var sourceKey = uomUfidaDto.ID.ToString();
            if (unitCacheDict.TryGetValue(sourceKey, out var unitDto))
            {
                return unitDto;
            }

            unitDto = await UnitAppService.FindBySourceKeyAsync(sourceKey, SourceConfig.SourceType);
            if (unitDto == null)
            {
                unitDto = await CreateUnitAsync(uomUfidaDto);
            }

            if (unitDto != null)
            {
                unitCacheDict[sourceKey] = unitDto;
            }

            return unitDto;
        }

        private async Task<Dictionary<string, UnitDto>> BatchLoadUnits(List<string> sourceKeys)
        {
            if (sourceKeys == null || sourceKeys.Count == 0)
                return new Dictionary<string, UnitDto>();

            var query = await UnitRepository.GetQueryableAsync();
            var items = await AsyncExecuter.ToListAsync(
                query.Where(x =>
                    sourceKeys.Contains(x.SourceKey) && x.SourceType == SourceConfig.SourceType
                )
            );

            return items.ToDictionary(x => x.SourceKey, x => ObjectMapper.Map<Unit, UnitDto>(x));
        }

        private async Task<UnitDto> CreateUnitAsync(UomUfidaDto uomUfidaDto)
        {
            var unitCreateDto = new UnitCreateDto
            {
                SourceType = SourceConfig.SourceType,
                SourceKey = uomUfidaDto.ID.ToString(),
                Code = uomUfidaDto.Code,
                DisplayName = uomUfidaDto.Name,
                SourceCreatedOn = uomUfidaDto.CreatedOn,
            };

            return await UnitAppService.CreateAsync(unitCreateDto);
        }

        private decimal GetRate(UomUfidaDto uomUfidaDto, List<UomUfidaDto> items)
        {
            if (uomUfidaDto.IsBase)
                return 1;

            var baseUom = items.FirstOrDefault(x => x.ID == uomUfidaDto.BaseUOM);
            if (baseUom == null)
                return 1;

            return uomUfidaDto.RatioToBase * GetRate(baseUom, items);
        }

        private (int mainToAux, int auxToMain) GetRatios(decimal rate)
        {
            if (rate <= 0)
            {
                return (1, 1);
            }

            if (rate < 1)
            {
                var main = SafeConvertToInt(1 / rate);
                return (main, 1);
            }

            if (rate > 1)
            {
                var aux = SafeConvertToInt(rate);
                return (1, aux);
            }

            return (1, 1);
        }

        private int SafeConvertToInt(decimal value)
        {
            if (value <= 0)
            {
                return 1;
            }

            if (value >= int.MaxValue)
            {
                return int.MaxValue;
            }

            return Convert.ToInt32(value);
        }
    }
}
